package fr.gdi.android.news.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.net.URLConnection;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import fr.gdi.android.news.utils.Constants.Preferences;
import fr.gdi.android.news.widgets.ScrollableNewsWidget;

public class IOUtils
{
    public static final String NL = System.getProperty("line.separator");
    
    public static final String EXTERNAL_STORAGE = "/Android/data/fr.gdi.android.news";
    
    private IOUtils()
    {
    }
    
    public static void writeFile(String folder, String filename, String content) throws Exception
    {
        File file = getFile(folder, filename);
        
        BufferedWriter out = null;
        try
        {
            out = new BufferedWriter(new FileWriter(file));
            out.write(content);
        }
        finally
        {
            out.close();
        }
    }
    
    public static String readFile(String folder, String filename) throws Exception
    {
        File file = getFile(folder, filename);
        
        StringBuilder content = new StringBuilder();
        BufferedReader in = null;
        try
        {
            in = new BufferedReader(new FileReader(file));
            String str = null;
            while ((str = in.readLine()) != null)
            {
                content.append(str).append(NL);
            }
            return content.toString();
        }
        finally
        {
            in.close();
        }
    }
    
    public static File getFile(String folder, String filename)
    {
        File sdcard = Environment.getExternalStorageDirectory();
        File dir = new File(sdcard.getAbsolutePath(), folder);
        dir.mkdirs();
        if (!TextUtils.isEmpty(filename))
        {
            File file = new File(dir, filename);
            return file;
        }
        return dir;
    }
    
    public static File getFolder(String folder)
    {
        return getFile(folder, null);
    }
    
    public static byte[] getBytes(InputStream stream) throws Exception
    {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        
        int n = 0;
        while (-1 != (n = stream.read(buffer)))
        {
            output.write(buffer, 0, n);
        }
        
        byte[] data = output.toByteArray();
        
        return data;
    }
    
    public static void closeStream(InputStream inputStream)
    {
        if (inputStream != null)
        {
            try
            {
                inputStream.close();
            }
            catch (Exception e)
            {
                // swallow
            }
        }
    }
    
    public static void closeStream(OutputStream outputStream)
    {
        if (outputStream != null)
        {
            try
            {
                outputStream.close();
            }
            catch (Exception e)
            {
                // swallow
            }
        }
    }
    
    public static SoftReference<Bitmap> getCachedImage(Context ctx, String url, Integer appWidgetId)
    {
        int thumbSize = PreferenceUtils.getIntPreference(ctx, Preferences.THUMB_SIZE, 64, appWidgetId);
        int minWidth = PreferenceUtils.getIntFromStringPreference(ctx, Preferences.MIN_THUMB_WIDTH, appWidgetId, "150");
        
        //int cropLength = PreferenceUtils.getCropLength(ctx, appWidgetId);
        //if ( cropLength < 0 ) thumbSize = ScrollableNewsWidget.getApproximateWidth(ctx, appWidgetId);
        
        File full = getImageCacheFile(url, "full");
        Bitmap fullImage = null;
        if (full.exists())
        {
            fullImage = BitmapFactory.decodeFile(full.getAbsolutePath());
            int width = fullImage.getWidth();
            if ( width < minWidth ) 
            {
                return null;
            }
        }
        
        File thumb = getImageCacheFile(url, Integer.toString(thumbSize));
        if (thumb.exists())
        {
            Bitmap bmp = BitmapFactory.decodeFile(thumb.getAbsolutePath());
            return new SoftReference<Bitmap>(bmp);
        }
        
        if (full.exists())
        {
            int width = fullImage.getWidth();
            if ( width >= minWidth ) 
            {
                Bitmap scaled = writeImage(ctx, fullImage, url, appWidgetId);
                if (scaled != null)
                {
                    return new SoftReference<Bitmap>(scaled);
                }
            }
        }
        
        return null;
    }
    
    public static SoftReference<Bitmap> getFullSizeCachedImage(Context ctx, String url)
    {
        File thumb = getImageCacheFile(url, "full");
        if (thumb.exists())
        {
            Bitmap bmp = BitmapFactory.decodeFile(thumb.getAbsolutePath());
            return new SoftReference<Bitmap>(bmp);
        }
        
        return null;
    }
    
    public static File getImageCacheFile(String url, String thumbSize)
    {
        String hash = Utils.getHash(url);
        File cacheDir = getCacheDir();
        
        File folder = new File(cacheDir, "thumbnails/" + thumbSize);
        if (!folder.exists()) folder.mkdirs();
        
        File thumb = new File(folder, hash);
        
        return thumb;
    }
    
    public static File getCacheDir()
    {
        File cacheDir = Environment.getExternalStorageDirectory();
        File folder = new File(cacheDir, EXTERNAL_STORAGE + "/.cache");
        return folder;
    }
    
    public static void relocateExternalStorage()
    {
        String deprecatedLocation = "/snw";
        File cacheDir = Environment.getExternalStorageDirectory();
        File destDir = new File(cacheDir, EXTERNAL_STORAGE);
        File sourceDir = new File(cacheDir, deprecatedLocation); 
        copyDirectory(sourceDir, destDir);
        delete(sourceDir);
    }
    
    public static void clearThumbnailCache() 
    {
        File cacheDir = getCacheDir();
        
        File folder = new File(cacheDir, "thumbnails/");
        
        delete(folder);
    }
    
    public static void copyDirectory(File sourceDir, File destDir)
    {
        if ( !sourceDir.exists() ) return;
        
        if( !destDir.exists() )
        {
            destDir.mkdir();
        }
        
        try
        {
            File[] children = sourceDir.listFiles();
            for( File sourceChild : children )
            {
                String name = sourceChild.getName();
                File destChild = new File(destDir, name);
                if(sourceChild.isDirectory())
                {
                    copyDirectory(sourceChild, destChild);
                }
                else
                {
                    copyFile(sourceChild, destChild);
               }
            }
        }
        catch ( Exception e ) 
        {
            Log.e(IOUtils.class.getName(), "Unable to relocate cache", e);
        }
    }
    
    public static void copyFile(File source, File dest) throws Exception
    {
        if( !dest.exists() ) 
        {
            dest.createNewFile();
        }
        
        InputStream in = null;
        OutputStream out = null;
        try
        {
             in = new FileInputStream(source);
             out = new FileOutputStream(dest);
       
            byte[] buf = new byte[1024];
            int len;
            while((len = in.read(buf)) > 0)
            {
                out.write(buf, 0, len);
            }
        }
        finally
        {
            in.close();
            out.close();
        }
    }

    
    public static Bitmap writeImage(Context context, Bitmap bmp, String url, Integer appWidgetId)
    {
        if (bmp == null) return null;
        
        int height = bmp.getHeight();
        int width = bmp.getWidth();
        
        int thumbSize = PreferenceUtils.getIntPreference(context, Preferences.THUMB_SIZE, 64, appWidgetId);
        
        //int cropLength = PreferenceUtils.getCropLength(context, appWidgetId);
        //if ( cropLength < 0 ) thumbSize = ScrollableNewsWidget.getApproximateWidth(context, appWidgetId);
        
        
        int w, h;
        if (width == height)
        {
            w = h = thumbSize;
        }
        else if (width > height)
        {
            w = thumbSize;
            h = height * thumbSize / width;
        }
        else
        {
            h = thumbSize;
            w = width * thumbSize / height;
        }
        
        Bitmap scaled = Bitmap.createScaledBitmap(bmp, w, h, false);
        
        File thumb = IOUtils.getImageCacheFile(url, Integer.toString(thumbSize));
        File full = IOUtils.getImageCacheFile(url, "full");
        FileOutputStream fos = null, fos2 = null;
        
        try
        {
            fos = new FileOutputStream(thumb);
            scaled.compress(Bitmap.CompressFormat.PNG, 85, fos);
            fos.flush();
            
            if (!full.exists())
            {
                fos2 = new FileOutputStream(full);
                bmp.compress(Bitmap.CompressFormat.PNG, 85, fos2);
                fos2.flush();
            }
            
            return scaled;
        }
        catch (Exception e)
        {
            Log.e(IOUtils.class.getName(), "Error while writing thumbnail to cache", e);
            // swallow?
            return null;
        }
        finally
        {
            IOUtils.closeStream(fos);
            IOUtils.closeStream(fos2);
        }
    }
    
    public static byte[] getRemoteURLBytes(String urlSpec)
    {
        
        InputStream stream = null;
        try
        {
            stream = getRemoteURLStream(urlSpec);
            return IOUtils.getBytes(stream);
        }
        catch (Exception e)
        {
            Log.e(IOUtils.class.getName(), "Unable to fetch image", e);
            return null;
        }
        finally
        {
            IOUtils.closeStream(stream);
        }
    }
    
    public static InputStream getRemoteURLStream(String urlSpec)
    {
        try
        {
            URL url = new URL(urlSpec);
            URLConnection connection = url.openConnection();
            connection.setRequestProperty("User-Agent", "SNW/1.0");
            connection.setUseCaches(true);
            connection.connect();
            
            return connection.getInputStream();
        }
        catch (Exception e)
        {
            Log.e(IOUtils.class.getName(), "Unable to fetch image", e);
            return null;
        }
    }

    //until java 7
    public static boolean isSymlink(File file)  
    {
        if (file == null) 
        {
            throw new NullPointerException("File must not be null");
        }

        try
        {
            File canon;
            
            if (file.getParent() == null) 
            {
                canon = file;
            } 
            else 
            {
                File canonDir = file.getParentFile().getCanonicalFile();
                canon = new File(canonDir, file.getName());
            }
            
            return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
        }
        catch ( Exception e ) 
        {
            return false;
        }
    }
    
    public static void delete(File f)
    {
        if ( !f.exists() ) return;
        
        if ( isSymlink(f) ) return;
        
        if ( f.isFile() ) 
        {
            f.delete();
            return;
        }
        
        File[] files = f.listFiles();
        
        //just in case
        if ( files == null ) return;
        
        for (int i = 0; i < files.length; i++)
        {
            delete(files[i]);
        }
        
        f.delete();
    }
}
